﻿// Inner Fire 游戏引擎库
// 顶点类
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 把顶点从DirectX中剥离出来（至少以后全平台时好改）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-03-19

#pragma once

#include <DirectXMath.h>
#include <cstdint>
#include <klog.h>
#include <knumber.h>

namespace ifire {

struct Vertex {
  DirectX::XMFLOAT3 pos;
  DirectX::XMFLOAT3 normal;
  DirectX::XMFLOAT2 texcoord;
  DirectX::XMFLOAT3 tangent;
};

struct UIVertex {
  DirectX::XMFLOAT2 pos;
  DirectX::XMFLOAT2 uv;
};

struct SkinnedVertex {
  DirectX::XMFLOAT3 pos;
  DirectX::XMFLOAT3 normal;
  DirectX::XMFLOAT2 texcoord;
  DirectX::XMFLOAT3 tangent;
  DirectX::XMFLOAT4 bone_weights;
  uint16_t bone_indices[4];

  SkinnedVertex()
      : pos(0.0f, 0.0f, 0.0f)
      , normal(0.0f, 0.0f, 0.0f)
      , texcoord(0.0f, 0.0f)
      , tangent(0.0f, 0.0f, 0.0f)
      , bone_weights(0.0f, 0.0f, 0.0f, 0.0f)
      , bone_indices{0, 0, 0, 0} {}

  void AddBoneData(uint16_t bone_id, float weight) {
    // 依次设置x,y,z值
    if (bone_weights.x == 0.0f) {
      bone_indices[0] = bone_id;
      bone_weights.x = weight;
      return;
    }
    if (bone_weights.y == 0.0f) {
      bone_indices[1] = bone_id;
      bone_weights.y = weight;
      return;
    }
    if (bone_weights.z == 0.0f) {
      bone_indices[2] = bone_id;
      bone_weights.z = weight;
      return;
    }
    if (bone_weights.w == 0.0f) {
      bone_indices[3] = bone_id;
      bone_weights.w = weight;
    }
  }
  // 归一化所有的Weights
  void NormalizeWeights() {
    auto total_weights =
        bone_weights.x + bone_weights.y + bone_weights.z + bone_weights.w;
    bone_weights.x = bone_weights.x / total_weights;
    bone_weights.y = bone_weights.y / total_weights;
    bone_weights.z = bone_weights.z / total_weights;
    bone_weights.w = bone_weights.w / total_weights;
  }
};

// 每个顶点最多包含多少骨骼数据。4个是一个效率适中的选择。
constexpr size_t MAX_BONES_PER_VERTEX = 4;

struct VertexBoneData {
  // 用于保存骨骼的ID
  unsigned int bones[MAX_BONES_PER_VERTEX] = {0};
  // 用于保存骨骼的ID对应的权重
  float weights[MAX_BONES_PER_VERTEX] = {0.0f};

  // 初始化所有项目为0
  VertexBoneData() {};

  // 加入一个骨骼数据
  void Add(unsigned int id, float weight) {
    // 如果项目不存在，则进行设置
    for (size_t i = 0; i < MAX_BONES_PER_VERTEX; i++) {
      if (bones[i] == 0) {
        bones[i] = id;
        weights[i] = weight;
      }
    }
  }
};

} // namespace ifire